home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 4 / MacMania 4.toast / / Tools&Utilities / unshar-19 / unshar.c < prev    next >
Text File  |  1995-07-21  |  22KB  |  918 lines

  1. /*
  2.     File:        unshar.c
  3.  
  4.     Contains:    unshar for MPW that's good enough for
  5.                 comp.sources.unix archives
  6.  
  7.     Written by:    sw
  8.                 Network Analysis Ltd
  9.                 178 Wainbody Ave South
  10.                 Coventry CV3 6BX
  11.                 UK
  12.     Phone:        +44 1203 419996
  13.     E-mail:        sw@network-analysis-ltd.co.uk
  14.     
  15.     Copyright:    Public domain
  16.  
  17.     Change History (most recent first):
  18.  
  19.     <11+>        21/07/95    sw        Add mods for Metrowerks compiler.
  20.     <11>        30/06/95    sw        Minor mods for PPCC
  21.     <10>        30/06/95    sw        Changed to work with TransSkel 3.x, univ headers,
  22.                                     Metrowerks and native PPC.
  23.     <9>            18/11/91    sw        Changes to compile with TC 5.0.
  24.     <8>            18/11/91    sw        Deal with embedded "cd"s
  25.     <7>             19/5/91    sw        Remove tests for "if"s and make less sensitive to
  26.                                     variations in shar file formats. Also stop
  27.                                     looking for #! /bin/sh; any comment line will do.
  28.                                     Merge MPW and Think C vers into 1 src file.
  29.     <6>             16/5/91    sw        Handle shar files in sumex archives.
  30.     <5>              3/5/91    sw        Previous mod didn't get the last char of the
  31.                                     filename.
  32.     <4>              3/5/91    sw        Cope with shar files that do not quote the
  33.                                     filename in the "if test -f" line.
  34.     <3>              3/2/91    sw        Pick up terminating string from "sed" or "cat"
  35.                                     command line.
  36.     <2>              5/7/90      sw        Reconvert to MPW tool
  37.     <1>              ??????    aw        Original by Amanda Walker, Intercon
  38.  
  39.     To Do:
  40. */
  41. #ifdef    MPW
  42. #include <Types.h>
  43. #include <StdDef.h>
  44. #include <CursorCtl.h>
  45. #include <Strings.h>
  46. #endif    MPW
  47. #include <Files.h>
  48. #include <Errors.h>
  49. #include <Script.h>
  50. #include <errno.h>
  51. #include <stdio.h>
  52. #include <stdlib.h>
  53. #include <String.h>
  54. #ifndef NIL
  55. #define NIL    (0L)
  56. #endif
  57.  
  58. /* Strings used in error messages */
  59. #ifdef    MPW
  60. #define    CPSTR        c2pstr
  61. #define PCSTR        p2cstr
  62. #else
  63. #define CPSTR        CtoPstr
  64. #define PCSTR        PtoCstr
  65. #endif
  66.  
  67. #ifndef EOF
  68. #define EOF    (-1L)
  69. #endif
  70.  
  71. #ifndef    MPW
  72. /*    for Think C/Metrowerks
  73. */
  74. #include <StandardFile.h>
  75. #include "TransSkel.h"
  76.  
  77. #define DIRECTORY(pb)    (((pb).dirInfo.ioFlAttrib & 0x10) == 0x10)
  78. #define    getDir        14            /* buttons in the dialogue box */
  79. #define    getCurDir    15
  80. #define    GD_PROMPT    16
  81.  
  82. typedef enum
  83. {
  84.     infoDlgRes = 1000,
  85.     aboutAlrtRes,            /* About... alert resource number */
  86.     abortRes,
  87.     gdDlgRes,
  88.     typeDlgRes,
  89.     errorAlertRes,
  90.     dupFNAlertRes
  91. };
  92.  
  93.  
  94. typedef enum                /* File menu item numbers */
  95. {
  96.     extract = 1,
  97.     close,
  98.     quit
  99. };
  100.  
  101.  
  102. typedef enum                 /* Edit menu item numbers */
  103. {
  104.     undo = 1,
  105.     /* --- */
  106.     cut = 3,
  107.     copy,
  108.     paste,
  109.     clear
  110. };
  111.  
  112. typedef enum                /* Option menu item numbers */
  113. {
  114.     forceOpt = 1,
  115.     setCr
  116. };
  117.  
  118. typedef enum                /* dialog item numbers */
  119. {
  120.     okB = 1,
  121.     crText,
  122.     cancelB
  123. };
  124.  
  125. typedef enum
  126. {
  127.     continueB = 1,
  128.     quitB,
  129.     messageItem
  130. };
  131.  
  132. MenuHandle    fileM, editM, optM;
  133. DialogPtr    infoDlgPtr, typeDlgPtr;
  134.  
  135. #endif                                /* Think C declarations */
  136.  
  137. Boolean force =     false;          /* force overwriting existing files */
  138. OSType    fdCreator =    'MPS ';            /* Finder Creator */
  139. OSType    fdType =    'TEXT';            /* Finder Type */
  140.  
  141. void ErrMsg (unsigned char *p1, char *p2);
  142.                                     /* for display errors */
  143. void ConvertFName (char *unixfilename, char *mpwfilename);
  144.                                     /* for converting Unix->Mac filenames */
  145. void unshar(char *s);                /* the guts of the program */
  146.  
  147. #ifndef    MPW
  148. /*    Prototypes for standalone version */
  149. void SetDText(DialogPtr dlog, short item, Str255 str);
  150. void GetDText(DialogPtr dlog, short item, StringPtr str);
  151. void GetCreator(void);
  152. void DoFileMenu(short item);
  153. void DoEditMenu(short item);
  154. void DoOptMenu(short item);
  155. void DoAbout(void);
  156. pascal short DirSelHook(short item, DialogPtr theDialog, Ptr dataP);
  157. pascal Boolean DirFilterProc(CInfoPBPtr pb, Ptr dataP);
  158. Boolean GetDir(char *text, FSSpec* dirSpec);
  159. void Extract(void);
  160.  
  161. // AE handlers
  162. pascal OSErr MyAEQuitApp (const AppleEvent *theAEvt, AppleEvent *replyEvt, long refCon);
  163. pascal OSErr MyAEOpenDoc (const AppleEvent *theAEvt, AppleEvent *replyEvt, long refCon);
  164. pascal void     MyAEProc (EventRecord *theEvent);            // called by TransSkel
  165. OSErr MyGotRequiredParams(const AppleEvent* Event);
  166.  
  167. DlgHookYDUPP                dlgHookUPP        = NULL;
  168. FileFilterYDUPP                dlgFilterUPP    = NULL;
  169. static AEEventHandlerUPP    aeQuitProc, aeOpenDocProc;
  170.  
  171. void SetDText(DialogPtr dlog, short item, Str255 str)
  172. {
  173. Handle    itemHandle;
  174. short    itemType;
  175. Rect    itemRect;
  176.  
  177.     GetDItem (dlog, item, &itemType, &itemHandle, &itemRect);
  178.     SetIText (itemHandle, str);
  179. }
  180.  
  181. /* Dialog handler */
  182.  
  183. void GetDText(DialogPtr dlog, short item, StringPtr str)
  184. {
  185. Handle    itemHandle;
  186. short    itemType;
  187. Rect    itemRect;
  188.  
  189.     GetDItem (dlog, item, &itemType, &itemHandle, &itemRect);
  190.     GetIText (itemHandle, str);
  191. }
  192.  
  193.  
  194. void GetCreator(void)
  195. {
  196.     short    itemHit;
  197.     char    creator[5];
  198.  
  199.     typeDlgPtr = GetNewDialog (typeDlgRes, NIL, (WindowPtr) -1L);
  200.     BlockMove(&fdCreator, creator+1, 4);
  201.     creator[0] = '\004';
  202.     SetDText(typeDlgPtr, crText, (StringPtr)creator);
  203.     SelectWindow(typeDlgPtr);
  204.     ShowWindow(typeDlgPtr);
  205.     DrawDialog(typeDlgPtr);
  206.     ModalDialog(NIL, &itemHit);
  207.     if (itemHit == okB) {
  208.         GetDText (typeDlgPtr, crText,(StringPtr) &creator);
  209.         BlockMove(creator+1, &fdCreator, 4);
  210.     }
  211.     DisposDialog(typeDlgPtr);
  212. }
  213. /*
  214.     AE handler called by TransSkel
  215. */
  216. pascal void MyAEProc (EventRecord *theEvent)
  217. {
  218.     // we only care about AEs for which we have handlers
  219.     AEProcessAppleEvent (theEvent);
  220. }
  221.  
  222.  
  223. static pascal OSErr MyAEQuitApp (const AppleEvent *theAEvt, AppleEvent *replyEvt, long refCon)
  224. {
  225.     OSErr                    OSError;
  226.     
  227.     OSError = MyGotRequiredParams(theAEvt);
  228.     if (OSError == noErr)
  229.         SkelStopEventLoop();    // stop TransSkel
  230.     
  231.     return (OSError);
  232. }
  233.  
  234.  
  235. static pascal OSErr MyAEOpenDoc (const AppleEvent *theAEvt, AppleEvent *replyEvt, long refCon)
  236. {
  237.     OSErr        err = noErr;
  238.     AEDesc        argv;
  239.     short        vRefNum;
  240.     long        dirID;
  241.  
  242.     HGetVol(NIL, &vRefNum, &dirID);
  243.     
  244.     // pick up each arg and extract it
  245.     err = AEGetParamDesc(theAEvt, keyDirectObject, typeAEList, &argv);
  246.     if (err == noErr) {
  247.         long                    argc;
  248.         
  249.         err = AECountItems(&argv, &argc);
  250.         if (err == noErr)
  251.         {
  252.             long        bytesNeeded = 0;
  253.             long        index = 0;\
  254.             AEKeyword    keyword;
  255.             DescType    dataType;
  256.             long        actualSize;
  257.             FSSpec        theFSSpec;
  258.  
  259.             for (index = 1; index <= argc && err == noErr; index++)
  260.             {
  261.                 err = AEGetNthPtr(&argv, index, typeFSS,
  262.                                   &keyword, &dataType, &theFSSpec,
  263.                                   sizeof(theFSSpec), &actualSize);
  264.                 if (err == noErr)
  265.                 {
  266.                     // get current dir
  267.                     (void) HSetVol(NIL, theFSSpec.vRefNum, theFSSpec.parID);
  268.                     PtoCstr(theFSSpec.name);
  269.                     unshar((char *)theFSSpec.name);
  270.                 }
  271.             }
  272.         }
  273.         
  274.         (void)AEDisposeDesc(&argv);
  275.         (void) HSetVol(NIL, vRefNum, dirID);
  276.     }
  277.     return (err);
  278. }
  279.  
  280. static OSErr MyGotRequiredParams(const AppleEvent* Event)
  281. {
  282.     OSErr        err;
  283.     DescType    ReturnedType;
  284.     Size        ActualSize;
  285.     
  286.     err = AEGetAttributePtr(Event, keyMissedKeywordAttr, typeWildCard, &ReturnedType, NULL, 0, &ActualSize);
  287.     if (err == errAEDescNotFound) {
  288.         
  289.         err = noErr;
  290.         
  291.     } else if (err == noErr) {
  292.         
  293.         err = errAEEventNotHandled;
  294.     }
  295.     
  296.     return (err);
  297. }
  298.  
  299.  
  300.  
  301. /*
  302.     File menu handler
  303. */
  304.  
  305. void DoFileMenu(short item)
  306. {
  307. WindowPeek    wPeek;
  308.  
  309.     switch (item)
  310.     {
  311.         case extract:
  312.             Extract();
  313.             break;
  314.         case close:
  315.             if ((wPeek = (WindowPeek) FrontWindow ()) != NIL)
  316.             {
  317.                 if (wPeek->windowKind < 0)
  318.                     CloseDeskAcc (wPeek->windowKind);
  319.             }
  320.             break;
  321.         case quit:
  322.             SkelStopEventLoop ();
  323.             break;
  324.     }
  325. }
  326.  
  327.  
  328. void DoEditMenu(short item)
  329. {
  330. DialogPtr    theDialog;
  331.  
  332.     theDialog = (DialogPtr) FrontWindow ();
  333.     if (((WindowPeek) theDialog)->windowKind != dialogKind)
  334.         return;
  335.  
  336.     switch (item)
  337.     {
  338.         case cut:
  339.         {
  340.             DlgCut (theDialog);
  341.             (void) ZeroScrap ();
  342.             (void) TEToScrap ();
  343.             break;
  344.         }
  345.  
  346.         case copy:
  347.         {
  348.             DlgCopy (theDialog);
  349.             (void) ZeroScrap ();
  350.             (void) TEToScrap ();
  351.             break;
  352.         }
  353.  
  354.         case paste:
  355.         {
  356.             (void) TEFromScrap ();
  357.             DlgPaste (theDialog);
  358.             break;
  359.         }
  360.  
  361.         case clear:
  362.         {
  363.             DlgDelete (theDialog);
  364.             break;
  365.         }
  366.     }
  367. }
  368.  
  369. void DoOptMenu(short item)
  370. {
  371.     switch (item)
  372.     {
  373.         case forceOpt:    force = !force;
  374.                         CheckItem(optM, forceOpt, force);
  375.                         break;
  376.          case setCr:        GetCreator();
  377.                          break;
  378.      }
  379. }
  380.  
  381. /*
  382.     Handle selection of About… item from Apple menu
  383. */
  384.  
  385. void DoAbout(void)
  386. {
  387.     (void) Alert (aboutAlrtRes, NIL);
  388. }
  389.  
  390. Boolean        useCurDir;        /* Set if current dir to be used */
  391.  
  392. /* Filter procs & dialogue hooks to select directories only in SFGet file */
  393. pascal short DirSelHook(short item, DialogPtr theDialog, Ptr dataP)
  394. {
  395.     if (GetWRefCon((WindowPtr)theDialog) != sfMainDialogRefCon)
  396.         return item;
  397.     if (item == getDir|| item == getCurDir) {
  398.         /* folder selected */
  399.         *(Boolean*)dataP = (item == getCurDir);
  400.         item = sfItemOpenButton;
  401.     }
  402.     return(item);
  403. }
  404.  
  405. pascal Boolean DirFilterProc(CInfoPBPtr pb, Ptr dataP)
  406. {
  407.     return(!DIRECTORY(*pb));    /* a directory if bit 4 is set */
  408. }
  409. /*
  410.  * GetDir - manage the directory selection dialog
  411.  */
  412. Boolean GetDir(char *text, FSSpec* dirSpec)
  413. {
  414.     Point                where = {-1, -1};
  415.     StandardFileReply    reply = {0};
  416.     CInfoPBRec            pb = {0};
  417.     DialogPtr            dlgP;
  418.     Boolean                useCurDir    = false;
  419.     short                dirVRefNum;
  420.     
  421.  
  422.     ParamText((StringPtr)text, (StringPtr)"\p", (StringPtr)"\p", (StringPtr)"\p");
  423.     CustomGetFile(dlgFilterUPP,
  424.                   -1,
  425.                   NIL,
  426.                   &reply,
  427.                   gdDlgRes,
  428.                   where,
  429.                   dlgHookUPP,
  430.                   NIL,
  431.                   NIL,
  432.                   NIL,
  433.                   (Ptr)&useCurDir);
  434.     if (reply.sfGood)
  435.     {
  436.         *dirSpec = reply.sfFile;
  437.         if (useCurDir) {
  438.             dirSpec->parID = LMGetCurDirStore();
  439.         }
  440.         else {
  441.             // the reply contains vref, dir ID and name of selected folder
  442.             pb.dirInfo.ioNamePtr    = (StringPtr)&reply.sfFile.name;
  443.             pb.dirInfo.ioVRefNum    = reply.sfFile.vRefNum;
  444.             pb.dirInfo.ioDrDirID    = reply.sfFile.parID;
  445.             PBGetCatInfo(&pb, false);
  446.             dirSpec->parID            = pb.dirInfo.ioDrDirID;
  447.         }
  448.     }
  449.     return reply.sfGood;
  450. }
  451.  
  452. void Extract(void)
  453. {
  454.     StandardFileReply    reply;
  455.     short                curVRef;
  456.     long                curDir;
  457.  
  458.     /*
  459.      * Use the standard file dialog to select the archive.
  460.      */
  461.     StandardGetFile(NIL, -1, NIL, &reply);
  462.     if (!reply.sfGood)
  463.         return;
  464.     HGetVol(NIL, &curVRef, &curDir);
  465.     HSetVol(NIL, reply.sfFile.vRefNum, reply.sfFile.parID);
  466.     PCSTR(reply.sfFile.name);
  467.     unshar((char*)reply.sfFile.name);
  468.     HSetVol(NIL, curVRef, curDir);
  469. }
  470.  
  471. main(int argc, char *argv[])
  472. {
  473.  
  474.     Handle        fTypeH;
  475.     DialogTHndl    dlgH;
  476.     Point        where;
  477.     short        ht, wd;
  478.         
  479.     SkelInit (NIL);
  480.     SkelApple ("\pAbout Unshar…", (SkelMenuSelectProcPtr)DoAbout);
  481.  
  482.     // init UPPs
  483.     dlgHookUPP        = NewDlgHookYDProc(DirSelHook);
  484.     dlgFilterUPP    = NewFileFilterYDProc(DirFilterProc);
  485.     aeQuitProc        = NewAEEventHandlerProc(MyAEQuitApp);
  486.     aeOpenDocProc    = NewAEEventHandlerProc(MyAEOpenDoc);
  487.  
  488.     fileM = NewMenu (1000, (StringPtr)"\pFile");
  489.     AppendMenu (fileM, (StringPtr)"\pExtract/O;Close/K;Quit/Q");
  490.     SkelMenu (fileM, (SkelMenuSelectProcPtr)DoFileMenu, NIL, FALSE, FALSE);
  491.  
  492.     editM = NewMenu (1001, (StringPtr)"\pEdit");
  493.     AppendMenu (editM, (StringPtr)"\p(Undo/Z;(-;Cut/X;Copy/C;Paste/V;Clear");
  494.     SkelMenu (editM, (SkelMenuSelectProcPtr)DoEditMenu, NIL, FALSE, FALSE);
  495.  
  496.     optM = NewMenu (1002, (StringPtr)"\pOptions");
  497.     AppendMenu (optM, (StringPtr)"\pOverwrite existing files;File type...");
  498.     SkelMenu (optM, (SkelMenuSelectProcPtr)DoOptMenu, NIL, FALSE, TRUE);
  499.     
  500.     // install AEs
  501.     if (SkelQuery (skelQHasAppleEvents))
  502.     {
  503.         /* Apple Events are available */
  504.         if (AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
  505.                                    aeQuitProc, 0L, false) != noErr)
  506.         {
  507.             // croak and die
  508.             ExitToShell();
  509.         }
  510.         if (AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments,
  511.                                    aeOpenDocProc, 0L, false) != noErr)
  512.         {
  513.             // croak and die
  514.             ExitToShell();
  515.         }
  516.         SkelSetAEHandler (MyAEProc);        // register with TransSkel
  517.     }
  518.     else
  519.         /* Apple Events are not available */
  520.     {
  521.         ExitToShell();                        // die
  522.     }
  523.  
  524.     // pick up default type and creator
  525.     if ((fTypeH = (char **)GetResource('ftyp', 0)) == NIL) {
  526.         ExitToShell();
  527.     }
  528.     BlockMove(*fTypeH, &fdCreator, 4);
  529.     BlockMove((*fTypeH)+4, &fdType, 4);
  530.     ReleaseResource(fTypeH);
  531.     
  532.     dlgH = (DialogTHndl)GetResource('DLOG', typeDlgRes);
  533.     wd = ((* dlgH)->boundsRect.right)-((* dlgH)->boundsRect.left);
  534.     /* centre the dialogue box on the screen
  535.        (but how do I know which screen?)
  536.     */
  537.     where.h = (qd.screenBits.bounds.right-qd.screenBits.bounds.left-wd) / 2;
  538.     (* dlgH)->boundsRect.right += where.h;
  539.     (* dlgH)->boundsRect.left = where.h;
  540.     
  541.     dlgH = (DialogTHndl)GetResource('DLOG', infoDlgRes);
  542.     wd = ((* dlgH)->boundsRect.right)-((* dlgH)->boundsRect.left);
  543.     /* centre the dialogue box on the screen
  544.        (but how do I know which screen?)
  545.     */
  546.     where.h = (qd.screenBits.bounds.right-qd.screenBits.bounds.left-wd) / 2;
  547.     (* dlgH)->boundsRect.right += where.h;
  548.     (* dlgH)->boundsRect.left = where.h;
  549.  
  550.     SkelEventLoop ();
  551.     SkelCleanup ();
  552. }
  553. #else                                /* MPW main support routines */
  554.  
  555. main(int argc, char *argv[])
  556. {
  557.     char    **filelist;                /* list of files to process */
  558.     int        fileCount = 0;            /* no of files to process */    
  559.  
  560. #ifdef    MPW
  561.     InitCursorCtl(nil);
  562. #endif    MPW
  563.     argc--; argv++;
  564.     if ((filelist = (char **)calloc((size_t)argc, sizeof(Ptr))) == NULL) {
  565.         fprintf(stderr, "### Not enough memory\n");
  566.         exit(-1);
  567.     }
  568.     while (argc) {
  569.         if (argv[0][0] == '-') {
  570.             switch (argv[0][1]) {
  571.             case '\0':
  572.                 filelist[fileCount++] = "-";
  573.                 break;
  574.             case 'c':
  575.                 /* creator is in next arg */
  576.                 argc--;
  577.                 argv++;
  578.                 strncpy((char *)&fdCreator, argv[0], (size_t)4);
  579.                 break;
  580.             case 'f':
  581.                 force = true;
  582.                 break;
  583.             case 't':
  584.                 /* type is in next arg */
  585.                 argc--;
  586.                 argv++;
  587.                 strncpy((char *)&fdType, argv[0], (size_t)4);
  588.                 break;
  589.             default:
  590.                 fprintf(stderr, "### Usage: unshar -f -c creator -t type [files|-]\n");
  591.                 exit(-1);
  592.             }
  593.         }
  594.         else
  595.             filelist[fileCount++] = argv[0];
  596.         argc--; argv++;
  597.     }
  598.     /* at this point all files to process are in filelist */
  599.     for (argc = 0; argc < fileCount; argc++) {
  600.         unshar(filelist[argc]);
  601.     }
  602.     exit(0);
  603. }
  604. #endif
  605.  
  606. /*    This part (mostly) common to both MPW and Think C */
  607.  
  608. void ErrMsg (unsigned char *p1, char *p2)
  609. {
  610. #ifdef    MPW
  611. #    ifdef applec
  612.     fprintf (stderr, "### %P %P\n", p1, p2);
  613. #    else
  614.     fprintf (stderr, "### %#s %#s\n", p1, p2);
  615. #    endif
  616. #else
  617.     ParamText((StringPtr)p1, (StringPtr)p2, (StringPtr)"\p", (StringPtr)"\p");
  618.     (void)StopAlert(errorAlertRes, NIL);
  619. #endif
  620. }
  621.  
  622. void ConvertFName (char *unixfilename, char *macfilename)
  623. {
  624.     char    *cp, *tp;
  625.     char    buf[256];
  626.     Boolean    slashSeen = false;
  627.     
  628.     /* make a Mac relative pathname */
  629.     /*    rules of the game:
  630.         'foo' -> :foo
  631.         "foo" -> :foo
  632.         `foo` -> :foo
  633.         ./foo -> :foo
  634.         foo/baz -> :foo:baz
  635.         .foo -> :_foo
  636.         .. -> ::
  637.         foo/./baz -> :foo:baz
  638.         /foo/baz -> :foo:baz       because we don't really want to create files at top vol level
  639.         foo:baz -> :foo/baz
  640.         rules apply recursively
  641.     */
  642.     
  643.     (void) strcpy(macfilename, ":");            /* initialize with a leading colon */
  644.     for (cp = buf, tp = unixfilename; *tp; tp++) {
  645.         switch (*tp) {
  646.             case '\'':
  647.             case '"':
  648.             case '`':    break;                    /* delete these chars */
  649.             case '.':    if (*(tp+1) == '/') {    /* delete any occurence of ./ */
  650.                             tp++;
  651.                         }
  652.                         else if (*(tp+1) == '.') {
  653.                             *cp++ = ':';
  654.                             *cp++ = ':';        /* convert .. to :: */
  655.                             tp++;
  656.                         }
  657.                         else if (cp == buf)
  658.                             *cp++ = '_';        /* replace leading dot with _ */
  659.                         else
  660.                             *cp++ = '.';
  661.                         break;                    
  662.             case '/':    if (cp != buf)
  663.                             *cp++ = ':';        /* replace / with : */
  664.                         slashSeen = true;
  665.                         break;
  666.             case ':':    *cp++ = '/';            /* replace : with / */
  667.                         break;
  668.             default:    *cp++ = *tp;
  669.                         break;
  670.         }
  671.     }
  672.     *cp = '\0';
  673.     
  674.     if (slashSeen)
  675.         (void) strcat(macfilename, buf);
  676.     else
  677.         (void) strcpy(macfilename, buf);
  678.     CPSTR(macfilename);
  679. }
  680.  
  681. void unshar(char* s)
  682. {
  683.   char        buffer[BUFSIZ];
  684.   char        *cp;
  685.   FILE        *infp;
  686.   short        outfp = -1;
  687.   char        unixfilename[256], mpwfilename[256];
  688.   char        *tp, *ts, delim;
  689.   char        terminator[30];
  690.   int        tlen;
  691.   int        line;
  692.   long        dirID, subDirID;
  693.   short        volRef;
  694.   FInfo        fileInfo;
  695.   OSErr        err;
  696.   FSSpec    extractedFSp;
  697.   FSSpec    defDir;
  698.  
  699. #ifdef    MPW
  700.   if (strcmp(s, "-") == 0) {
  701.       infp = stdin;
  702.     ErrMsg("\pProcessing std input:","");
  703.   }
  704.   else {
  705.       infp = fopen(s, "r");
  706.       CPSTR(s);
  707.       if (!infp) {
  708.         ErrMsg("\pCould not open file", s);
  709.         exit(-1);
  710.       }
  711.       else ErrMsg("\pProcessing", s);
  712.   }
  713.   HGetVol(NIL, &volRef, &dirID);        /* get current dir, vol */
  714.   FSMakeFSSpec(volRef, dirID, (StringPtr)"\p", &defDir);
  715. #else    /* Think C */
  716.   infp = fopen(s, "r");
  717.   CPSTR(s);
  718.   if (!infp) {
  719.     ErrMsg("\pCould not open file", s);
  720.     return;
  721.   }
  722. #endif
  723.  
  724.   /* skip over news header lines etc. */
  725.   for (line = 1; cp = fgets(buffer, sizeof(buffer), infp); line++)
  726.     if (buffer[0] == '#' || buffer[0] == ':') break;
  727.     
  728.   if (!cp) {
  729.     ErrMsg("\pCould not locate start of archive in file", s);
  730.     exit(-1);
  731.   }
  732.  
  733. #ifndef    MPW
  734.   /* Think C version needs to ask user to locate target folder */
  735.  
  736.   if (!GetDir((char*)"\pin which to put extracted files", &defDir))
  737.       return;
  738.   
  739.   dirID    = defDir.parID;
  740.  
  741.  
  742.   /*
  743.    * Set the default directory as a base to put everything.
  744.    */
  745.   (void)HSetVol(NIL, defDir.vRefNum, dirID);
  746.   
  747.   /* now we should be at the start of the shar archive itself */
  748.   infoDlgPtr = NIL;
  749.   infoDlgPtr = GetNewDialog (infoDlgRes, NIL, (WindowPtr)-1L);
  750. #endif
  751.  
  752.   while (cp = fgets(buffer, sizeof(buffer), infp)) {
  753.     line++;
  754. #ifdef    MPW
  755.     SpinCursor(-1);
  756. #endif    MPW
  757.  
  758.     if (buffer[0] == '#' || buffer[0] == ':') continue;    /* comment line */
  759.     if (strncmp(buffer, "exit", 4) == 0) { break; }  /* exit */
  760.  
  761.     /*    there are 2 types of shar files:
  762.         a) the ones that use "cat"
  763.         b) the ones that use "sed"
  764.     */
  765.     if (strncmp(buffer, "sed", 3) == 0 ||
  766.         strncmp(buffer, "cat", 3) == 0) {
  767.           sscanf(buffer, "%*[^>]>%s", unixfilename);
  768.           /* make Mac relative pathname */
  769.           ConvertFName(unixfilename, mpwfilename);
  770.  
  771.           /* work out the terminating string */
  772.           sscanf(buffer, "%*[^<]<<%s", terminator);
  773.           tp = terminator;
  774.           tlen = strlen(terminator);
  775.           
  776.           while (*tp == ' ') tp++;        /* skip whitespace */
  777.           switch (*tp) {
  778.               case '\\':    ts = tp + 1;    /* start of term string */
  779.                             delim = ' ';
  780.                             break;
  781.               case '"':        ts = tp + 1;
  782.                             delim = *tp;
  783.                             break;
  784.               case '\'':    ts = tp + 1;
  785.                             delim = *tp;
  786.                             break;
  787.               default:        ts = tp;
  788.                             delim = ' ';
  789.                             break;                
  790.           }
  791.           do {
  792.               tp++;
  793.           } while (*tp != '\0' && *tp != delim);
  794.           *tp = '\0';
  795.           tlen = tp - ts;
  796.           FSMakeFSSpec(0, dirID, (StringPtr)mpwfilename, &extractedFSp);
  797.           err = FSpOpenDF(&extractedFSp, fsWrPerm, &outfp);
  798.            if (err == noErr && !force) {
  799.             FSClose(outfp);
  800. #ifdef    MPW
  801.             ErrMsg("\pWill not overwrite existing file", mpwfilename);
  802. #else
  803.             ParamText((StringPtr)"\pWill not overwrite existing file",
  804.                       (StringPtr)mpwfilename, (StringPtr)"\p", (StringPtr)"\p");
  805.             if (NoteAlert(dupFNAlertRes, NIL) == quitB) break;
  806. #endif
  807.             while (strncmp(buffer, ts, tlen) != 0) {  /* skip to terminating string */
  808.               fgets(buffer, sizeof(buffer), infp);
  809.               line++;
  810.             }
  811.           } else {
  812.             if (outfp != -1) FSClose(outfp);
  813.             if ((err = FSpCreate(&extractedFSp, fdCreator, fdType, smSystemScript)) != noErr)
  814.             {
  815.                 if (err != dupFNErr || !force)
  816.                 {
  817.                     ErrMsg("\pCouldn't create", mpwfilename);
  818.                     exit(-1);
  819.                 }
  820.             }
  821. #ifdef MPW
  822. #    ifdef applec
  823.             fprintf(stderr, "  Open \"%P\"\n", mpwfilename);
  824. #    else
  825.             fprintf(stderr, "  Open \"%#s\"\n", mpwfilename);
  826. #    endif
  827. #else
  828.             ParamText((StringPtr)mpwfilename,
  829.                       (StringPtr)"\p", (StringPtr)"\p", (StringPtr)"\p");
  830.             SelectWindow(infoDlgPtr);
  831.             ShowWindow(infoDlgPtr);
  832.             DrawDialog(infoDlgPtr);         
  833. #endif
  834.             if (FSpOpenDF(&extractedFSp, fsWrPerm, &outfp) != noErr)
  835.             {
  836.                 ErrMsg("\pCouldn't open", mpwfilename);
  837.                 exit(-1);
  838.             }
  839.             
  840.             if (strncmp(buffer, "sed", 3) == 0) {
  841.                 fgets(buffer, sizeof(buffer), infp);
  842.                 do {
  843.                   long    count;
  844.                   count    = strlen(buffer+1);
  845. #ifdef    THINK_C
  846.                   if (buffer[count] == '\n')
  847.                       buffer[count] = '\r';
  848. #endif            
  849.                   FSWrite(outfp, &count, (Ptr)buffer+1);
  850.                   fgets(buffer, sizeof(buffer), infp);
  851.                   line++;
  852. #ifdef    MPW
  853.                   SpinCursor(1);
  854. #endif    MPW
  855.                 } while (strncmp(buffer, ts, tlen) != 0);
  856.             }
  857.             else {
  858.                 /* copy everything up to terminating string to output file */
  859.                 fgets(buffer, sizeof(buffer), infp);
  860.                 while (strncmp(buffer, ts, tlen) != 0) {
  861.                     long    count;
  862.                     count    = strlen(buffer);
  863. #ifdef    THINK_C
  864.                     if (count > 0 && buffer[count-1] == '\n')
  865.                         buffer[count-1] = '\r';
  866. #endif            
  867.                     FSWrite(outfp, &count, (Ptr)buffer);
  868.                     fgets(buffer, sizeof(buffer), infp);
  869.                     line++;
  870. #ifdef    MPW
  871.                     SpinCursor(1);
  872. #endif    MPW
  873.                 }
  874.             }
  875.             FSClose(outfp);
  876. #ifndef    MPW
  877.             HideWindow((WindowPtr)infoDlgPtr);
  878. #endif
  879.             /* set file type and creator */
  880.             GetFInfo((StringPtr)mpwfilename,0,&fileInfo);
  881.             fileInfo.fdType = fdType;
  882.             fileInfo.fdCreator = fdCreator;
  883.             SetFInfo((StringPtr)mpwfilename, 0, &fileInfo);
  884.           }
  885.       } else if (strncmp(buffer, "if test ! -d", 12) == 0 ||
  886.                    strncmp(buffer, "if `test ! -d", 13) == 0) {
  887.         /* testing to see if a directory is there */
  888.         if (sscanf(buffer, "if test ! -d '%s'", unixfilename) == 1 ||
  889.             sscanf(buffer, "if test ! -d %s", unixfilename) == 1 ||
  890.             sscanf(buffer, "if `test ! -d %s`", unixfilename) == 1) {
  891.           /* make Mac relative pathname */
  892.           ConvertFName(unixfilename, mpwfilename);
  893.             
  894.           /* I wish MPW C had mkdir(), but at least we don't have to do parameter blocks */
  895.           CPSTR(unixfilename);
  896.           HGetVol((StringPtr)unixfilename, &volRef, &dirID);
  897.           err = DirCreate(volRef, dirID, (StringPtr)mpwfilename, &subDirID);
  898.         }
  899.     } else if (strncmp(buffer, "cd ", 3) == 0) {
  900.         if (sscanf(buffer, "cd '%s'", unixfilename) == 1 ||
  901.             sscanf(buffer, "cd %s", unixfilename) == 1)     {
  902.             /* change the default directory */
  903.             ConvertFName(unixfilename, mpwfilename);
  904. #ifdef MPW
  905. #    ifdef applec
  906.             fprintf(stderr, "###  Change directory to \"%P\"\n", mpwfilename);
  907. #    else
  908.             fprintf(stderr, "###  Change directory to \"%#s\"\n", mpwfilename);
  909. #    endif
  910. #endif
  911.             HSetVol((StringPtr)mpwfilename, 0, 0L);
  912.             HGetVol((StringPtr)unixfilename, &volRef, &dirID);
  913.         }
  914.     }
  915.   }
  916.   fclose(infp);
  917. }
  918.